package cn.com.easy.persistence;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * mybatis json格式适配
 * <br/>用法：
 * mapper.xml中添加：
 * <resultMap id="basePlusResultMap" type="cn.com.platform.entity.JsonTestEntity">
*		<result property="user" column="user" typeHandler="cn.com.easy.persistence.JsonTypeHandlerMyBatis" />
*		<result property="users" column="users" typeHandler="cn.com.easy.persistence.JsonTypeHandlerMyBatis" />
*		<result property="userProperties" column="userProperties" typeHandler="cn.com.easy.persistence.JsonTypeHandlerMyBatis" />
*	</resultMap>
 * @author nibili 2019年12月28日上午11:11:49
 *
 * @param <T>
 */
public class JsonTypeHandlerMyBatis<T> extends BaseTypeHandler<T> {
	private final static Log log = LogFactory.getLog(JsonTypeHandlerMyBatis.class);
	private static ObjectMapper objectMapper;
	private Class<T> type;
	private Class<T> resultType;

	static {
		objectMapper = new ObjectMapper();
		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
	}

//	public JsonTypeHandlerMyBatis() {
//	
//	}
//	public JsonTypeHandlerMyBatis(Class<T> type) {
//		if (log.isTraceEnabled()) {
//			log.trace("JsonTypeHandler(" + type + ")");
//		}
//		if (type == null) {
//			throw new IllegalArgumentException("Type argument cannot be null");
//		}
//		this.type = type;
//	}

	public JsonTypeHandlerMyBatis(Class<T> type, Class<T> resultType) {
		if (log.isTraceEnabled()) {
			log.trace("JsonTypeHandler(" + type + ")");
		}
		if (type == null) {
			throw new IllegalArgumentException("Type argument cannot be null");
		}
		this.type = type;
		this.resultType = resultType;
	}

	private T parse(String json) {
		try {
			if (json == null || json.length() == 0) {
				return null;
			}
			return objectMapper.readValue(json, type);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	private String toJsonString(Object obj) {
		try {
			return objectMapper.writeValueAsString(obj);
		} catch (JsonProcessingException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public T getNullableResult(ResultSet rs, String columnName) throws SQLException {

		Field curField = getFieldByName(columnName, resultType);
		Class<?> curFieldType = curField.getType();
		// 集合List元素
		if (curFieldType.equals(List.class)) {
			// 当前集合的泛型类型
			Type genericType = curField.getGenericType();
			if (null != genericType) {

				if (genericType instanceof ParameterizedType) {
					ParameterizedType pt = (ParameterizedType) genericType;
					// 得到泛型里的class类型对象
					Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
					JavaType javaType = objectMapper.getTypeFactory().constructCollectionType(List.class,
							actualTypeArgument);

					try {
						String json = rs.getString(columnName);
						if (json == null || json.length() == 0) {
							return null;
						}
						return objectMapper.readValue(json, javaType);
					} catch (IOException e) {
						throw new RuntimeException(e);
					}
				}

			}
		} else if (curFieldType.equals(Map.class)) {
			// 当前Map的泛型类型
			Type genericType = curField.getGenericType();
			if (null != genericType) {

				if (genericType instanceof ParameterizedType) {
					ParameterizedType pt = (ParameterizedType) genericType;
					// 得到泛型里的class类型对象
					Class<?> key = (Class<?>) pt.getActualTypeArguments()[0];
					Class<?> value = (Class<?>) pt.getActualTypeArguments()[1];
					JavaType javaType = objectMapper.getTypeFactory().constructParametricType(Map.class, key, value);

					try {
						String json = rs.getString(columnName);
						if (json == null || json.length() == 0) {
							return null;
						}
						return objectMapper.readValue(json, javaType);
					} catch (IOException e) {
						throw new RuntimeException(e);
					}
				}

			}
		}

		return (T) parse(rs.getString(columnName));
	}

	@Override
	public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
		return (T) parse(rs.getString(columnIndex));
	}

	@Override
	public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
		return (T) parse(cs.getString(columnIndex));
	}

	@Override
	public void setNonNullParameter(PreparedStatement ps, int columnIndex, T parameter, JdbcType jdbcType)
			throws SQLException {
		ps.setString(columnIndex, toJsonString(parameter));
	}

	/**
	 * @MethodName : getFieldByName
	 * @Description : 根据字段名获取字段
	 * @param fieldName 字段名
	 * @param clazz     包含该字段的类
	 * @return 字段
	 */
	private static Field getFieldByName(String fieldName, Class<?> clazz) {
		// 拿到本类的所有字段
		Field[] selfFields = clazz.getDeclaredFields();

		// 如果本类中存在该字段，则返回
		for (Field field : selfFields) {
			if (field.getName().equals(fieldName)) {
				return field;
			}
		}

		// 否则，查看父类中是否存在此字段，如果有则返回
		Class<?> superClazz = clazz.getSuperclass();
		if (superClazz != null && superClazz != Object.class) {
			return getFieldByName(fieldName, superClazz);
		}

		// 如果本类和父类都没有，则返回空
		return null;
	}
}